import atexit
import concurrent
import json
import os.path
import sys

from concurrent.futures.thread import ThreadPoolExecutor
from datetime import datetime

import flask
from flask import request, jsonify, send_from_directory, render_template
from flask_cors import CORS
import logging

from FirmwareType import FirmwareType
from UpgradeStatus import UpgradeStatus
from ZijinUpgradeFirmware import upgrade_single_host, power_off, power_on
import ipaddress
from PowerType import PowerType

# 导入LMDB管理器
from lmdb_manger import LMDBManager


# 读取系统配置
def read_system_configs():
    with open(file="config.json", mode="r") as f:
        settings = json.load(f)
        print(f"Read {len(settings)} settings from config.json")
        print(f"Settings: {settings}")
        return settings


configs = read_system_configs()
# 当前代码将日志级别设置为字符串，需要改为实际的logging枚举值
try:
    # 使用getattr获取logging模块中对应级别的枚举值
    log_level = getattr(logging, configs["global"]["syslog_level"].upper())
    print(f"Logging level set to: {logging.getLevelName(log_level)}")
except (AttributeError, KeyError) as e:
    # 如果配置无效，使用默认级别INFO
    log_level = logging.INFO
    print(f"Invalid log level in config, using default (INFO). Error: {e}")


def setup_logging():
    """
    配置日志记录器，将日志保存到文件和控制台。
    :return: 配置好的日志记录器实例
    """
    # 检查并创建 firmware 文件夹
    if not os.path.exists("firmware"):
        os.makedirs("firmware")
    if not os.path.exists("logs"):
        os.makedirs("logs")

    # 配置日志，将日志保存到文件
    now_time = datetime.now().strftime('%Y%m%d%H%M%S')
    log_file = os.path.join("logs", f'app-{now_time}.log')

    # 打印日志文件路径，方便确认
    print(f"Log file path: {log_file}")

    try:
        # 检查日志文件是否可写
        with open(log_file, 'a'):
            pass
        print("Log file is writable.")
    except Exception as e:
        print(f"Failed to write to log file: {e}")
        return None

    logging.basicConfig(
        level=log_level,
        format='%(asctime)s - %(name)s - %(levelname)s - %(message)s',
        handlers=[
            logging.FileHandler(log_file, encoding='utf-8'),
            logging.StreamHandler()  # 保留控制台输出
        ]
    )
    return logging.getLogger(__name__)


logger = setup_logging()
if logger:
    logger.info("Logging system initialized.")
# 动态获取静态文件夹和模板文件夹路径
if getattr(sys, 'frozen', False):
    static_folder = os.path.join(sys._MEIPASS, 'dist')
    template_folder = os.path.join(sys._MEIPASS, 'dist')
else:
    static_folder = os.path.join(os.path.dirname(__file__), 'dist')
    template_folder = os.path.join(os.path.dirname(__file__), 'dist')

# 全局线程池
thread_pool = ThreadPoolExecutor(max_workers=configs["global"]["thread_pool_max_workers"])

app = flask.Flask(__name__, static_folder=static_folder, template_folder=template_folder)

# 启用 CORS，允许所有来源访问
CORS(app)

# 初始化LMDB数据库管理器
db_manager = LMDBManager()

if logger:
    logger.info("Flask application initialized.")


# 处理所有路由，返回 index.html
@app.route('/', defaults={'path': ''})
@app.route('/<path:path>')
def catch_all(path):
    """
    处理所有路由，返回 index.html
    :param path: 请求的路径
    :return: index.html
    """
    if path != "" and os.path.exists(os.path.join(app.static_folder, path)):
        return send_from_directory(app.static_folder, path)
    else:
        return send_from_directory(app.static_folder, 'index.html')


# 添加主机信息
@app.route('/hosts/add', methods=['POST'])
def add_host():
    """
    添加主机信息
    :return: 主机信息
    """
    data = request.get_json()
    # 检查 data 是否为字典类型
    if not isinstance(data, dict):
        return jsonify({'error': 'Invalid JSON data'}), 400
    required_fields = ['host_ip', 'username', 'password']
    # 检查是否包含所有必需字段
    for field in required_fields:
        if field not in data:
            return jsonify({'error': f'Missing required field: {field}'}), 400
    try:
        new_host = db_manager.add_host(
            host_ip=data['host_ip'],
            username=data['username'],
            password=data['password'],
        )
        return jsonify({'message': 'Host information added successfully'}), 201
    except Exception as e:
        return jsonify({'error': str(e)}), 400


@app.route('/hosts/batch_add', methods=['POST'])
def batch_add_host():
    """
    批量添加主机信息
    :return: 主机信息
    """
    data = request.get_json()
    start_ip = data['start_ip']
    end_ip = data['end_ip']
    username = data['username']
    password = data['password']
    ip_list = generate_ip_list(start_ip, end_ip)
    if not ip_list:
        return jsonify({'error': 'Invalid IP range'}), 400
    failed_hosts = []
    for ip in ip_list:
        try:
            db_manager.add_host(
                host_ip=ip,
                username=username,
                password=password
            )
        except Exception as e:
            failed_hosts.append(ip)
            logging.error(f"Failed to add host {ip}: {str(e)}")
    return jsonify(
        {'message': 'Host information added successfully', 'ip_list': ip_list, 'failed_hosts': failed_hosts}), 201


@app.route("/hosts/batch_delete", methods=['POST'])
def batch_delete_host():
    """
    批量删除主机信息
    :return: 主机信息
    """
    data = request.get_json()
    if not isinstance(data, dict):
        return jsonify({'error': 'Invalid JSON data'}), 400
    for ip in data['host_lists']:
        host = db_manager.get_host_by_id(ip['id'])
        if host:
            db_manager.delete_host(ip['id'])
            logging.debug(f"Host {ip} deleted successfully")
        if not host:
            return jsonify({'error': f'Host {ip} not found'}), 404
    return jsonify({'message': 'Host information deleted successfully'}), 200


def generate_ip_list(start_ip, end_ip):
    """
    生成 IP 地址列表
    :param start_ip: 起始 IP 地址
    :param end_ip: 结束 IP 地址
    :return: IP 地址列表
    """
    try:
        # 将输入的起始和结束 IP 转换为 IPv4 地址对象
        start_ip_obj = ipaddress.IPv4Address(start_ip)
        end_ip_obj = ipaddress.IPv4Address(end_ip)

        # 确保起始 IP 不大于结束 IP
        if start_ip_obj > end_ip_obj:
            return []

        # 生成 IP 地址列表
        ip_list = [str(ipaddress.IPv4Address(ip_int)) for ip_int in range(int(start_ip_obj), int(end_ip_obj) + 1)]
        return ip_list
    except ipaddress.AddressValueError:
        # 若 IP 地址格式错误，返回空列表
        return []


# 获取所有主机信息
@app.route('/hosts/get', methods=['GET'])
def get_all_hosts():
    """
    获取所有主机信息
    :return: 主机信息
    """
    hosts = db_manager.get_all_hosts()
    result = []
    for host in hosts:
        host_data = {
            'id': host['id'],
            'host_ip': host['host_ip'],
            'username': host['username'],
            'password': host['password'],
            'upgrade_status': host['upgrade_status'].name,
            'upgrade_log': host['upgrade_log'],
        }
        result.append(host_data)
    return jsonify(result)


@app.route('/hosts/update', methods=['POST'])
def update_host():
    """
    更新主机信息
    :return: 主机信息
    """
    data = request.get_json()
    host_id = data.get('id')
    host_ip = data.get('host_ip')
    username = data.get('username')
    password = data.get('password')
    if not host_id:
        return jsonify({'error': 'Host ID is required'}), 400
    if not host_ip:
        return jsonify({'error': 'Host IP is required'}), 400
    if not username:
        return jsonify({'error': 'Username is required'}), 400
    if not password:
        return jsonify({'error': 'Password is required'}), 400
    try:
        if db_manager.update_host(host_id, host_ip, username, password):
            return jsonify({'message': 'Host information updated successfully'}), 200
        else:
            return jsonify({'error': 'Host not found'}), 404
    except Exception as e:
        return jsonify({'error': str(e)}), 400


# 删除主机信息
@app.route('/hosts/delete/<int:id>', methods=['DELETE'])
def delete_host(id):
    """
    删除主机信息
    :param id: 主机 ID
    :return: 操作结果
    """
    if db_manager.delete_host(id):
        return jsonify({'message': 'Host information deleted successfully'}), 200
    else:
        return jsonify({'error': 'Host information not found'}), 404


# 上传固件文件
@app.route("/firmware/upload", methods=["POST"])
def upload_firmware():
    """
    上传固件文件
    :return: 操作结果
    """
    file_name = request.files['firmware'].filename
    # 检查固件是否已存在
    existing_firmware = db_manager.get_firmware_by_name(file_name)
    if existing_firmware:
        return jsonify({'error': 'Firmware file already exists'}), 400
    if "firmware" not in request.files:
        return jsonify({"error": "No firmware file provided"}), 400
    firmware_file = request.files["firmware"]
    if firmware_file.filename == "":
        return jsonify({"error": "No selected file"}), 400
    # 获取 type 字段
    file_type = request.form.get('type')
    if not file_type:
        return jsonify({"error": "Type field is required"}), 400
    if firmware_file:
        firmware_file.save(os.path.join("firmware", firmware_file.filename))
        # 将文件指针重置到文件开头
        firmware_file.seek(0)
        # 转换固件类型
        firmware_type = FirmwareType[file_type]
        firmware = db_manager.add_firmware(
            file_name=firmware_file.filename,
            file_path=os.path.join("firmware", firmware_file.filename),
            file_size=len(firmware_file.read()),
            firmware_type=firmware_type
        )
        return jsonify({"message": "Firmware uploaded successfully"})
    return None


@app.route("/firmware/list", methods=["GET"])
def get_all_firmware():
    """
    获取所有固件文件
    :return: 固件文件列表
    """
    firmware_files = []
    firmware_list = db_manager.get_all_firmware()
    if firmware_list:
        logging.info(f"firmware_list: {firmware_list}")
        for firmware in firmware_list:
            firmware_files.append(
                {
                    "id": firmware['id'],
                    "file_name": firmware['file_name'],
                    "file_path": firmware['file_path'],
                    "file_size": firmware['file_size'],
                    # 将 datetime 对象转换为字符串，避免 JSON 序列化错误
                    "upload_time": firmware['upload_time'].strftime("%Y-%m-%d %H:%M:%S"),
                    "type": firmware['type'].name
                }
            )
    else:
        logging.info("firmware_list is None")
    return jsonify(firmware_files)


@app.route("/firmware/delete/<int:id>", methods=["DELETE"])
def delete_firmware(id):
    """
    删除固件文件
    :param id: 固件 ID
    :return: 操作结果
    """
    logging.info(f"Received request to delete firmware with id: {id}")
    # 根据 id 从数据库中查询对应固件记录
    firmware = db_manager.get_firmware_by_id(id)
    if not firmware:
        return jsonify({"error": "Firmware record not found in database"}), 404

    firmware_path = firmware['file_path']
    if not os.path.exists(firmware_path):
        logging.warning(f"File not found on disk: {firmware_path}")
    try:
        if os.path.exists(firmware_path):
            # 删除磁盘上的文件
            os.remove(firmware_path)
        # 从数据库删除记录
        if db_manager.delete_firmware(id):
            return jsonify({"message": "Firmware deleted successfully"})
        else:
            return jsonify({"error": "Failed to delete firmware from database"}), 500
    except Exception as e:
        return jsonify({"error": f"Failed to delete firmware: {e}"}), 500


def upgrade_host(host_ip, firmware_lists, is_force_restart):
    """
    升级单个主机
    :param host_ip: 主机 IP
    :param firmware_lists: 固件列表
    :param is_force_restart: 是否强制重启
    :return: 升级结果
    """
    try:
        # 示例：更新主机升级状态为 "UPGRADING"
        host = db_manager.get_host_by_ip(host_ip)
        if not host:
            return f"Host {host_ip} not found"

        # 模拟实际升级过程（替换为真实升级逻辑）
        db_manager.update_host_status(host_ip, UpgradeStatus.IN_PROGRESS)

        query_firmware_lists = []
        if firmware_lists['biosFirmwareID'] != '':
            bios = db_manager.get_firmware_by_id(int(firmware_lists['biosFirmwareID']))
            query_firmware_lists.append({"type": bios['type'], "file_path": bios['file_path']})
        if firmware_lists['cpldFirmwareID'] != '':
            cpld = db_manager.get_firmware_by_id(int(firmware_lists['cpldFirmwareID']))
            query_firmware_lists.append({"type": cpld['type'], "file_path": cpld['file_path']})
        if firmware_lists['bmcFirmwareID'] != '':
            bmc = db_manager.get_firmware_by_id(int(firmware_lists['bmcFirmwareID']))
            query_firmware_lists.append({"type": bmc['type'], "file_path": bmc['file_path']})
        if firmware_lists['fpgaFirmwareID'] != '':
            fpga = db_manager.get_firmware_by_id(int(firmware_lists['fpgaFirmwareID']))
            query_firmware_lists.append({"type": fpga['type'], "file_path": fpga['file_path']})
        # 调用升级函数
        is_upgraded, is_upgraded_msg = upgrade_single_host(host_ip, host['username'], host['password'],
                                                           query_firmware_lists,
                                                           is_force_restart)
        if is_upgraded:
            # 更新最终状态
            db_manager.update_host_status(host_ip, UpgradeStatus.COMPLETED,
                                          f"{datetime.now().strftime('%Y-%m-%d %H:%M:%S')} {is_upgraded_msg}")
            return f"Host {host_ip} upgrade success"
        else:
            db_manager.update_host_status(host_ip, UpgradeStatus.FAILED,
                                          f"{datetime.now().strftime('%Y-%m-%d %H:%M:%S')} {is_upgraded_msg}")
            return f"Host {host_ip} upgrade failed"
    except Exception as e:
        # 记录失败状态
        db_manager.update_host_status(host_ip, UpgradeStatus.FAILED, str(e))
        return f"Host {host_ip} upgrade failed: {str(e)}"


def str_to_bool(s):
    """
    将字符串转换为布尔值。
    支持的值包括 "true", "false", "1", "0"，不区分大小写。
    其他值将返回 False。
    """
    if isinstance(s, str):
        s = s.lower()
        return s in ['true', '1']
    return bool(s)


@app.route("/upgrade", methods=["POST"])
def upgrade_firmware():
    """
    升级固件
    :return: 升级结果
    """
    """升级接口（关键修改点：使用线程池并传递上下文）"""
    host_lists = request.json["host_lists"]
    firmware_lists = request.json["firmware_lists"]
    # 从请求中获取 is_force_restart 并转换为布尔值
    is_force_restart = str_to_bool(request.json["is_force_restart"])
    if not firmware_lists:
        return jsonify({"error": "No firmware list provided"}), 400
    if not host_lists or not isinstance(host_lists, list):
        return jsonify({"error": "Invalid host list"}), 400

    # 使用线程池执行升级任务
    futures = [thread_pool.submit(upgrade_host, ip['host_ip'], firmware_lists, is_force_restart) for ip in host_lists]

    # 收集结果（可选，根据需求决定是否阻塞）
    results = []
    for future in concurrent.futures.as_completed(futures):
        try:
            results.append(future.result())
        except Exception as e:
            results.append(f"Error: {str(e)}")
    if is_force_restart:
        # 如果未勾选强制重启，返回请根据情况自行重启升级
        return jsonify(
            {"message": "Upgrade tasks is completed,Please restart yourself to complete the upgrade as appropriate!!",
             "results": results}), 200
    return jsonify({
        "message": "Upgrade tasks is completed",
        "results": results
    }), 200


def power_on_or_off(host_ip, power_type):
    """
    开关机
    :param host_ip: 主机 IP
    :param power_type: 开关机类型
    :return: 开关机结果
    """
    try:
        # 示例：更新主机升级状态为 "UPGRADING"
        host = db_manager.get_host_by_ip(host_ip)
        if not host:
            return f"Host {host_ip} not found"

        # 模拟实际升级过程（替换为真实升级逻辑）
        db_manager.update_host_status(host_ip, UpgradeStatus.IN_PROGRESS)

        is_completed, return_msg = False, None
        if power_type is PowerType.OFF:
            is_completed, return_msg = power_off(host_ip, host['username'], host['password'])
        elif power_type is PowerType.ON:
            is_completed, return_msg = power_on(host_ip, host['username'], host['password'])
        if is_completed:
            # 更新最终状态
            db_manager.update_host_status(host_ip, UpgradeStatus.COMPLETED,
                                          f"{datetime.now().strftime('%Y-%m-%d %H:%M:%S')} {return_msg}")
            return f"Host {host_ip} power success"
        else:
            db_manager.update_host_status(host_ip, UpgradeStatus.FAILED,
                                          f"{datetime.now().strftime('%Y-%m-%d %H:%M:%S')} {return_msg}")
            return f"Host {host_ip} power failed"
    except Exception as e:
        # 记录失败状态
        db_manager.update_host_status(host_ip, UpgradeStatus.FAILED, str(e))
        return f"Host {host_ip} power failed: {str(e)}"


@app.route("/hosts/power_off", methods=["POST"])
def batch_power_off():
    """
    批量关机
    :return: 关机结果
    """
    host_lists = request.json['host_lists']
    if not host_lists:
        if not host_lists:
            return jsonify({"error": "No host list provided"}), 400
        if not host_lists or not isinstance(host_lists, list):
            return jsonify({"error": "Invalid host list"}), 400
        # 使用线程池执行升级任务
    futures = [thread_pool.submit(power_on_or_off, ip['host_ip'], PowerType.OFF) for ip in host_lists]

    # 收集结果（可选，根据需求决定是否阻塞）
    results = []
    for future in concurrent.futures.as_completed(futures):
        try:
            results.append(future.result())
        except Exception as e:
            results.append(f"Error: {str(e)}")
    return jsonify({
        "message": "Run tasks is completed",
        "results": results
    }), 200


@app.route("/hosts/power_on", methods=["POST"])
def batch_power_on():
    """
    批量开机
    :return: 开机结果
    """
    host_lists = request.json['host_lists']
    if not host_lists:
        if not host_lists:
            return jsonify({"error": "No host list provided"}), 400
        if not host_lists or not isinstance(host_lists, list):
            return jsonify({"error": "Invalid host list"}), 400
        # 使用线程池执行升级任务
    futures = [thread_pool.submit(power_on_or_off, ip["host_ip"], PowerType.ON) for ip in host_lists]

    # 收集结果（可选，根据需求决定是否阻塞）
    results = []
    for future in concurrent.futures.as_completed(futures):
        try:
            results.append(future.result())
        except Exception as e:
            results.append(f"Error: {str(e)}")
    return jsonify({
        "message": "Run tasks is completed",
        "results": "results"
    }), 200


@atexit.register
def shutdown_thread_pool_on_exit():
    """当应用程序退出时关闭线程池"""
    try:
        thread_pool.shutdown(wait=True)
        if logger:
            logger.info("Thread pool has been shut down gracefully")
        # 关闭数据库连接
        db_manager.close()
        if logger:
            logger.info("Database connection has been closed")
    except Exception as e:
        if logger:
            logger.error(f"Error shutting down thread pool: {str(e)}")


# 信号处理函数（针对生产环境）
def handle_shutdown_signal(signum, frame):
    """处理关闭信号"""
    if logger:
        logger.info(f"Received signal {signum}, shutting down...")
    sys.exit(0)  # 退出程序会触发 atexit 注册的函数


if __name__ == "__main__":
    app.run(debug=False, host='0.0.0.0', port=5000)
